#include "WebServer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_http_server.h"
#include "esp_spiffs.h"
#include "esp_log.h"
#include "esp_err.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_netif.h"
#include <stdlib.h>

#include "lwip/sockets.h"
#include "lwip/inet.h"
#include "lwip/api.h"

#include "WiFiIno_flash.h"
#include "Datapara.h"

static const char *TAG = "spiffs";


#define BUFFER_LEN  2048  

#define MAX_REQUESTS_PER_MINUTE 10

typedef struct 
{
    char data[BUFFER_LEN];
    int len;
    int client_socket;
}DATA_PARCEL;

static httpd_handle_t web_server_handle = NULL;//ws服务器唯一句柄

#define WEBSERVER_PORT 80  //WEB服务器端口号

static QueueHandle_t  ws_server_rece_queue = NULL;//收到的消息传给任务处理
static QueueHandle_t  ws_server_send_queue = NULL;//异步发送队列

/*此处只是管理ws socket server发送时的对象，以确保多客户端连接的时候都能收到数据，并不能限制HTTP请求*/
#define WS_CLIENT_QUANTITY_ASTRICT 5    //客户端数量
static int WS_CLIENT_LIST[WS_CLIENT_QUANTITY_ASTRICT];//客户端套接字列表
static int WS_CLIENT_NUM = 0;   //实际连接数量

/*客户端列表 记录客户端套接字*/
static void ws_client_list_add(int socket)
{
    /*检查是否超出限制*/
    if (WS_CLIENT_NUM>=WS_CLIENT_QUANTITY_ASTRICT)
    {
        return;
    }
    
    /*检查是否重复*/
    for (size_t i = 0; i < WS_CLIENT_QUANTITY_ASTRICT; i++) 
    {
        if (WS_CLIENT_LIST[i] == socket) {
            return;
        } 
    }

    /*添加套接字至列表中*/
    for (size_t i = 0; i < WS_CLIENT_QUANTITY_ASTRICT; i++) 
    {
        if (WS_CLIENT_LIST[i] <= 0){
            WS_CLIENT_LIST[i] = socket; //获取返回信息的客户端套接字
            printf("ws_client_list_add:%d\r\n",socket);
            WS_CLIENT_NUM++;
            return;
        }
    }
}

/*客户端列表 删除客户端套接字*/
static void ws_client_list_delete(int socket)
{
    for (size_t i = 0; i < WS_CLIENT_QUANTITY_ASTRICT; i++)
    {
        if (WS_CLIENT_LIST[i] == socket)
        {
            WS_CLIENT_LIST[i] = 0;
            printf("ws_client_list_delete:%d\r\n",socket);
            WS_CLIENT_NUM--;
            if (WS_CLIENT_NUM<0)
            {
                WS_CLIENT_NUM = 0;
            }
            break;
        }
    }
}

static DATA_PARCEL ws_rece_parcel;  
/*ws服务器接收数据*/
static esp_err_t ws_server_rece_data(httpd_req_t *req)
{
    if (req->method == HTTP_GET) {
        ws_client_list_add(httpd_req_to_sockfd(req));
        return ESP_OK;
    }
    esp_err_t ret = ESP_FAIL;
    httpd_ws_frame_t ws_pkt;
    memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
    memset(&ws_rece_parcel, 0, sizeof(DATA_PARCEL));
    ws_pkt.type = HTTPD_WS_TYPE_TEXT;
    ws_pkt.payload = (uint8_t*)ws_rece_parcel.data;   //指向缓存区
    ret = httpd_ws_recv_frame(req, &ws_pkt, 0);//设置参数max_len = 0来获取帧长度
    if (ret != ESP_OK) {
        printf("ws_server_rece_data data receiving failure!");
        return ret;
    }
    if (ws_pkt.len>0) 
    {
        ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len);/*设置参数max_len 为 ws_pkt.len以获取帧有效负载 */
        if (ret != ESP_OK) {
            printf("ws_server_rece_data data receiving failure!");
            return ret;
        }
        ws_rece_parcel.len = ws_pkt.len;
        ws_rece_parcel.client_socket = httpd_req_to_sockfd(req);
        if (xQueueSend(ws_server_rece_queue ,&ws_rece_parcel,pdMS_TO_TICKS(1))){
            ret = ESP_OK;
        }
    }
    else 
    {
        printf("ws_pkt.len<=0");
    }
    return ret;
}

/*WEB SOCKET*/
static const httpd_uri_t ws = {
    .uri        = "/ws",
    .method     = HTTP_GET,
    .handler    = ws_server_rece_data,
    .user_ctx   = NULL,
    .is_websocket = true
};


/*首页HTML GET处理程序    将该文件挂载到spiffs上直接访问    将首页处理程序放在前面处理，避免访问"/"时，后面的网页对其造成干扰"*/
static esp_err_t home_get_handler(httpd_req_t *req)
{

  
    // 读取HTML文件内容
        char *home_file_path = "/spiffs/web_client.html";
        xSemaphoreTake(file_mutex, portMAX_DELAY);
        FILE *fp = fopen(home_file_path, "r");
        if (fp == NULL) {
            xSemaphoreGive(file_mutex);
            // 文件打开失败，发送错误响应
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
        fseek(fp, 0, SEEK_END);
        long int file_size = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        char *content = (char *)malloc(file_size + 1);
        if (content == NULL) {
            fclose(fp);
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        size_t read_size = fread(content, 1, file_size, fp);
        content[read_size] = '\0';
        fclose(fp);
        httpd_resp_send(req, content, read_size);
        free(content);
        xSemaphoreGive(file_mutex);

        return ESP_OK;

}

/*首页HTML*/
static const httpd_uri_t home = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = home_get_handler,
    .user_ctx  = NULL
};


/*WIFI配置HTML GET处理程序 */
static esp_err_t configwifi_get_handler(httpd_req_t *req)
{
    extern const unsigned char upload_script_start[] asm("_binary_web_cfgwifi_html_start");/*web_client.html文件在bin中的位置*/
    extern const unsigned char upload_script_end[]   asm("_binary_web_cfgwifi_html_end");
    const size_t upload_script_size = (upload_script_end - upload_script_start);/*web_client.html文件在bin中的大小*/
    httpd_resp_send(req, (const char *)upload_script_start, upload_script_size);/*发送web_client.html文件到浏览器*/
    return ESP_OK;
}

/*首页HTML*/
static const httpd_uri_t cfgwifi = {
    .uri       = "/configwifi",
    .method    = HTTP_GET,
    .handler   = configwifi_get_handler,
    .user_ctx  = NULL
};


static esp_err_t tags_json_get_handler(httpd_req_t *req)
{

        char *json_path = "/spiffs/tags.json"; // 替换为实际的JSON文件路径
        FILE *fp = fopen(json_path, "r");
        if (fp == NULL) {
            // 文件打开失败，发送错误响应
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
        fseek(fp, 0, SEEK_END);
        long int file_size = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        char *json_data = (char *)malloc(file_size + 1);
        if (json_data == NULL) {
            fclose(fp);
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        size_t read_size = fread(json_data, 1, file_size, fp);
        json_data[read_size] = '\0';
        fclose(fp);
        httpd_resp_set_type(req, "application/json");
        httpd_resp_send(req, json_data, read_size);
        free(json_data);
        return ESP_OK;


}

/*自定义Json数据*/
static const httpd_uri_t tags_json = {
    .uri       = "/spiffs",
    .method    = HTTP_GET,
    .handler   = tags_json_get_handler,
    .user_ctx  = NULL
};

/*http事件处理*/
static void ws_event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
{
    if (event_base == ESP_HTTP_SERVER_EVENT )
    {
        switch (event_id)
        {
            case HTTP_SERVER_EVENT_ERROR ://当执行过程中出现错误时，将发生此事件
                break;
            case HTTP_SERVER_EVENT_START  ://此事件在HTTP服务器启动时发生
                break;
            case HTTP_SERVER_EVENT_ON_CONNECTED  ://一旦HTTP服务器连接到客户端，就不会执行任何数据交换
                break;
            case HTTP_SERVER_EVENT_ON_HEADER  ://在接收从客户端发送的每个报头时发生
                break;
            case HTTP_SERVER_EVENT_HEADERS_SENT  ://在将所有标头发送到客户端之后
                break;
            case HTTP_SERVER_EVENT_ON_DATA  ://从客户端接收数据时发生
                break;
            case HTTP_SERVER_EVENT_SENT_DATA ://当ESP HTTP服务器会话结束时发生
                break;
            case HTTP_SERVER_EVENT_DISCONNECTED  ://连接已断开
                esp_http_server_event_data* event = (esp_http_server_event_data*)event_data;
                ws_client_list_delete(event->fd);
                break;
            case HTTP_SERVER_EVENT_STOP   ://当HTTP服务器停止时发生此事件
                break;
        }
    }
}


static DATA_PARCEL async_buffer = {0};
/*异步发送函数，将其放入HTTPD工作队列*/
static void ws_async_send(void *arg)
{
    if (xQueueReceive(ws_server_send_queue,&async_buffer,0))
    {
        httpd_ws_frame_t ws_pkt ={0};
        ws_pkt.payload = (uint8_t*)async_buffer.data;
        ws_pkt.len = async_buffer.len;
        ws_pkt.type = HTTPD_WS_TYPE_TEXT;
        httpd_ws_send_frame_async(web_server_handle, async_buffer.client_socket, &ws_pkt) ;
    }
}

/*ws 发送函数*/
static DATA_PARCEL send_buffer;
static void ws_server_send(const char * data ,uint32_t len , int client_socket)
{
    memset(&send_buffer,0,sizeof(send_buffer));
    send_buffer.client_socket = client_socket;
    send_buffer.len = len;
    memcpy(send_buffer.data,data,len);
    xQueueSend(ws_server_send_queue ,&send_buffer,pdMS_TO_TICKS(1));
    httpd_queue_work(web_server_handle, ws_async_send, NULL);//进入排队
}


/*数据发送任务，每隔一秒发送一次    早期测试数据上传用的，现在没用了*/
static void ws_server_send_task(void *p)
{
    char buf[50] ;
    while (1)
    {
        memset(buf,0,sizeof(buf));
        // sprintf(buf,"%.2fcm", UploadingDisData);

        for (size_t i = 0; i < WS_CLIENT_QUANTITY_ASTRICT; i++)
        {
            if (WS_CLIENT_LIST[i]>0)
            {
                ws_server_send(buf,strlen(buf),WS_CLIENT_LIST[i]);
            } 
        }
        
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

/*数据接收处理任务*/
static DATA_PARCEL rece_buffer;   


static void ws_server_rece_task(void *p)
{
    while (1)
    {
        if(xQueueReceive(ws_server_rece_queue,&rece_buffer,portMAX_DELAY))
        {
            printf("socket : %d\tdata len : %d\tpayload : %s\r\n", rece_buffer.client_socket, rece_buffer.len, rece_buffer.data);
            
            //创建一个函数解析任务
            BaseType_t xReturn = xTaskCreate(DataPara, "DataPara Task", 1024 * 12, rece_buffer.data, 1, NULL);
            if (xReturn!= pdPASS)
            {
                // 任务创建失败，记录错误信息
                printf("xTaskCreate(DataPara Task) failed!\r\n");
                
                free(rece_buffer.data); 
                
            }
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }
    
}

void SpiffsInit()
{
    ESP_LOGI(TAG, "Initializing SPIFFS");

    esp_vfs_spiffs_conf_t conf = {
      .base_path = "/spiffs",   //可以认为挂着点，后续使用C库函数fopen("/spiffs/...")
      .partition_label = NULL,  //指定spiffs分区，如果为NULL，则默认为分区表中第一个spiffs类型的分区
      .max_files = 5,           //最大可同时打开的文件数
      .format_if_mount_failed = true
    };

        //初始化和挂载spiffs分区
    esp_err_t ret = esp_vfs_spiffs_register(&conf);


    //失败处理
    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return;
    }

    //执行SPIFFS文件系统检查
    ESP_LOGI(TAG, "Performing SPIFFS_check().");
    ret = esp_spiffs_check(conf.partition_label);//操作spiffs文件系统器件断电，可能会导致 SPIFFS 损坏，可通过esp_spiffs_check恢复
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));
        return;
    } else {
        ESP_LOGI(TAG, "SPIFFS_check() successful");
    }

    //获取SPIFFS可用区域大小
    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
        esp_spiffs_format(conf.partition_label);
        return;
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }

    //可用空间异常，执行SPIFFS检查
    if (used > total) {
        ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check().");
        ret = esp_spiffs_check(conf.partition_label);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));
            return;
        } else {
            ESP_LOGI(TAG, "SPIFFS_check() successful");
        }
    }

}

// 注册所有 URI 处理程序的函数
void register_uri_handlers(httpd_handle_t web_server_handle)
{
    httpd_register_uri_handler(web_server_handle, &home);//注册uri处理程序
    httpd_register_uri_handler(web_server_handle, &ws);//注册uri处理程序
    httpd_register_uri_handler(web_server_handle, &tags_json);//注册uri处理程序
    httpd_register_uri_handler(web_server_handle, &cfgwifi);//注册uri处理程序
    
}

/*web服务器切换模式处理函数*/
void webserver_sta_start_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {

    // 先停止当前模式下的 WebServer（如果有）
    if (web_server_handle!= NULL) {
        httpd_stop(web_server_handle);
    }
    // 重新初始化 WebServer 为 station 模式，使用统一端口号和注册 URI 处理程序
    httpd_config_t config_sta = HTTPD_DEFAULT_CONFIG();
    config_sta.max_open_sockets = 5;
    config_sta.server_port = WEBSERVER_PORT;
    config_sta.lru_purge_enable = true; //开启了按照最近最少使用策略来对缓存数据进行清理的功能
    if (httpd_start(&web_server_handle, &config_sta) == ESP_OK) {
        register_uri_handlers(web_server_handle);
        ESP_LOGI(TAG, "WebServer started in STA mode");
    }

}

void webserver_ap_start_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    // 先停止当前模式下的 WebServer（如果有）
    if (web_server_handle!= NULL) {
        httpd_stop(web_server_handle);
    }
    // 重新初始化 WebServer 为 station 模式，使用统一端口号和注册 URI 处理程序
    httpd_config_t config_ap = HTTPD_DEFAULT_CONFIG();
    config_ap.max_open_sockets = 5;
    config_ap.server_port = WEBSERVER_PORT;
    config_ap.lru_purge_enable = true; //开启了按照最近最少使用策略来对缓存数据进行清理的功能
    if (httpd_start(&web_server_handle, &config_ap) == ESP_OK) {
        register_uri_handlers(web_server_handle);
        ESP_LOGI(TAG, "WebServer started in AP mode");
    }
}

/*web服务器初始化*/
void web_server_init(void)
{

    SpiffsInit();

    esp_event_handler_instance_register(ESP_HTTP_SERVER_EVENT,ESP_EVENT_ANY_ID, &ws_event_handler,NULL,NULL);//任何事件注册处理程序

    /*创建接收队列*/
    ws_server_rece_queue = xQueueCreate(  3 , sizeof(DATA_PARCEL)); 
    if (ws_server_rece_queue == NULL )
    {
        printf("ws_server_rece_queue ERROR\r\n");

        // 尝试释放可能已经分配的其他资源，比如已经创建的发送队列（如果有的话）
        if (ws_server_send_queue!= NULL)
        {
            vQueueDelete(ws_server_send_queue);
            ws_server_send_queue = NULL;
        }
        // 可以根据具体情况，在这里添加更多的错误处理逻辑，比如返回错误码给调用者等
    }

    /*创建发送队列*/
    ws_server_send_queue = xQueueCreate(  3 , sizeof(DATA_PARCEL)); 
    if (ws_server_send_queue == NULL )
    {
        printf("ws_server_send_queue ERROR\r\n");

          // 如果接收队列已经创建成功，先删除接收队列以释放内存
        if (ws_server_rece_queue!= NULL)
        {
            vQueueDelete(ws_server_rece_queue);
            ws_server_rece_queue = NULL;
        }
        // 同样可以添加更多错误处理逻辑，如返回错误码等
    }

    BaseType_t xReturn ;
    /*创建接收处理任务*/
    xReturn = xTaskCreatePinnedToCore(ws_server_rece_task, "ws_server_rece_task", 4096, NULL, 15, NULL, tskNO_AFFINITY);
    if(xReturn != pdPASS) 
    {
        printf("xTaskCreatePinnedToCore ws_server_rece_task error!\r\n");
        // 如果接收队列和发送队列已经创建成功，删除它们以释放内存
        if (ws_server_rece_queue!= NULL)
        {
            vQueueDelete(ws_server_rece_queue);
            ws_server_rece_queue = NULL;
        }
        if (ws_server_send_queue!= NULL)
        {
            vQueueDelete(ws_server_send_queue);
            ws_server_send_queue = NULL;
        }
        // 可以添加更多错误处理逻辑，如返回错误码给调用者等
    }
    /*创建发送任务*/
    xReturn = xTaskCreatePinnedToCore(ws_server_send_task,"ws_server_send_task",4096,NULL,15,NULL, tskNO_AFFINITY);
    if(xReturn != pdPASS) 
    {
        printf("xTaskCreatePinnedToCore ws_server_send_task error!\r\n");
         // 如果接收队列和发送队列已经创建成功，删除它们以释放内存
        if (ws_server_rece_queue!= NULL)
        {
            vQueueDelete(ws_server_rece_queue);
            ws_server_rece_queue = NULL;
        }
        if (ws_server_send_queue!= NULL)
        {
            vQueueDelete(ws_server_send_queue);
            ws_server_send_queue = NULL;
        }
        // 同样可以添加更多错误处理逻辑，如返回错误码给调用者等
    }
}
